local tostring = tostring
local string = string
local table = table
local assert = assert
local tonumber = tonumber

local iconv = require("iconv")

--just for test
--require("std")
--local print = print

module("atcmd")


--AT test cmd
--expect return "OK\n"
AT = function() return "AT\r" end

--ATE0 : turn off echo
--"ATE0" or "ATE" turn off,"ATE1" turn on
--expect return "OK\n"
AT_ECHO_OFF = function() return "ATE0\r" end

--AT+CMGF: set phone to PDU mode
--"AT+CMGF=0\n"
AT_CMGF_SET_PDU = function() return "AT+CMGF=0\r" end

--AT+CGMM: get model identification of phone
--"AT+CGMM\n"
AT_CGMM = function() return "AT+CGMM\r" end

--AT+CGSN:get IMEI number of phone
--"AT+CGSN\n"
AT_CGSN = function() return "AT+CGSN\r" end

function semi_octets(num)
    local t = {}
    local c
    for i = 1,num:len() do
        c = string.char(num:byte(i))
        t[#t+1] = c
    end
    if (#t%2 ==1) then t[#t+1] = "F" end
    local temp
    for i = 1,#t/2 do
        temp = t[2*i]
        t[2*i] = t[2*i-1]
        t[2*i-1] = temp
    end
    return table.concat(t)
end

--print(semi_octets("13959292927"))

--AT+CMGS: send SMS
--see GSM 0705/0340/0338/0707
function AT_CMGS_PDU(num,data,data_encoding)

    local phone_number_format
    if string.char(num:byte(1))=="+" then
        phone_number_format = "91" --91 indicates international format of the phone number
    else
        phone_number_format = "81" --"81" (hex), unknown phone number format
    end
    
    num = num:gmatch("%+?(%d+)")()
    if num==nil then return end
    
    local cd = iconv.new("UTF-16BE" .. "//TRANSLIT", data_encoding)
    local user_data_ucs2, err = cd:iconv(data)
    assert(err==nil)
    
    function string_hex(s)
        local t = {}
        for i = 1,s:len() do
            t[#t+1] = string.format("%02X",s:byte(i))
        end
        return table.concat(t)
    end
    
    local user_data_ucs2_hex = string_hex(user_data_ucs2)
    
    local t =
    {
        --http://www.dreamfabric.com/sms/
        
        "00",--Length of SMSC information,
        "15",--First octet of the SMS-SUBMIT message, http://www.dreamfabric.com/sms/submit_fo.html
        "00",--TP-Message-Reference. The "00" value here lets the phone set the message reference number itself. 
        string.format("%02X",string.len(num)),--Address-Length. Length of phone number
        phone_number_format,--phone number format, http://www.dreamfabric.com/sms/type_of_address.html
        semi_octets(num),--The phone number in semi octets
        "00",--TP-PID. Protocol identifier. http://www.dreamfabric.com/sms/pid.html
        "08",--UCS2. TP-DCS,GSM0338,http://www.dreamfabric.com/sms/dcs.html
        "AA",--TP-Validity-Period. "AA" means 4 days. http://www.dreamfabric.com/sms/vp.html
        string.format("%02X",string.len(user_data_ucs2)),--TP-User-Data-Length
        user_data_ucs2_hex,--TP-User-Data
    }
    local sms_pdu = table.concat(t)
    return string.format("AT+CMGS=%d\r",(sms_pdu:len()/2-1)),sms_pdu..string.char(0x1A)--0x1A is ^Z
end

--print(AT_CMGS_PDU("13959292927","test一个","UTF-8"))

--AT+CPMS="ME"/"SM",set phonebook memory storage
--phone return OK
--AT+CPMS? ,to query the status of phonebook memory
--phone return example:'+CPBS: "ME", 25, 1000'
function AT_CPBS(storage)
    if storage then
        return string.format('AT+CPBS="%s"\r',storage)
    else
        return "AT+CPBS?\r"
    end
end

--parse AT+CPMS="ME" return string
function get_storage_cap_AT_CPBS(s)
    return s:match('+CPBS:%s*"([%a%d]+)",%s*(%d+),%s*(%d+)')
end


--AT+CSCS="UCS2" ,tell TA that which charset TE use
--GSM/HEX/IRA/PCCP437/UCS2/8859-1
--phone return OK
--AT+CSCS? ,query the setting
function AT_CSCS(charset)
    if charset then
        return string.format('AT+CSCS="%s"\r',charset)
    else
        return "AT+CSCS?\r"
    end
end

--AT+CPBR=1,read phonebook
--return '+CPBR: 3, "13600000000", 129, "0070"'
--index,number,ntype,name
--number type related spec is GSM 0408 10.5.4.7
--to check if it is international number: tonumber(ntype)%128/16)==1
function AT_CPBR(indexes)
    return string.format("AT+CPBR=%s\r",tostring(indexes))
end

function hex2ucs2(s)
    local byte_string_table = {}
    for b in s:gmatch("[%da-fA-F][%da-fA-F]") do
        byte_string_table[#byte_string_table+1] = string.char(tonumber("0x"..b))
    end
    local name = table.concat(byte_string_table)
    return name
end


--parse AT+CPBR=1 return string
function get_number_and_name_AT_CPBR(s)
    local index,number,type,name = s:match('+CPBR:%s*(%d+),%s*"(%d+)",%s*(%d+),%s*"([%da-fA-F]+)"')
    if (name~=nil) then
        name = hex2ucs2(name)
    end
    return index,number,type,name
end


--just for print debug info,I do not like many newline
--just print it as "\x0d\x0a"
function repr(s)
    local s2 = tostring(s)
    
    local function repr(c)
        c2 = string.byte(c)
        if (c2>=33 and c2<=128) then
            return c
        else
            return string.format('\\x%02x',c2)
        end
    end
    local result = s2:gsub('.', repr )
    
    return '"'..result..'"'
end
